/**
 * 
 */
package com.miolr.module.core.api;

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.codehaus.jackson.map.BeanDescription;
import org.codehaus.jackson.map.BeanProperty;
import org.codehaus.jackson.map.JsonSerializer;
import org.codehaus.jackson.map.SerializationConfig;
import org.codehaus.jackson.map.Serializers.Base;
import org.codehaus.jackson.map.TypeSerializer;
import org.codehaus.jackson.map.type.CollectionType;
import org.codehaus.jackson.map.type.MapType;
import org.codehaus.jackson.map.type.TypeFactory;
import org.codehaus.jackson.type.JavaType;
import org.hibernate.collection.internal.PersistentMap;
import org.hibernate.collection.spi.PersistentCollection;
import org.hibernate.proxy.HibernateProxy;

/**
 * @author iday
 * 
 */
public class HibernateSerializers extends Base {
	public HibernateSerializers() {
		super();
	}

	@Override
	public JsonSerializer<?> findSerializer(SerializationConfig config,
			JavaType type, BeanDescription beanDesc, BeanProperty property) {
		Class<?> raw = type.getRawClass();

		/*
		 * 13-Jul-2012, tatu: There's a bug in Jackson 2.0 which will call this
		 * method in some cases for Collections (and Maps); let's skip those
		 * cases and wait for the "real" call
		 */
		if (Collection.class.isAssignableFrom(raw)
				|| Map.class.isAssignableFrom(raw)) {
			return null;
		}

		/*
		 * Note: PersistentCollection does not implement Collection, so we may
		 * get some types here; most do implement Collection too however
		 */
		if (PersistentCollection.class.isAssignableFrom(raw)) {
			// TODO: handle iterator types? Or PersistentArrayHolder?
			JavaType elementType = _figureFallbackType(config, type);
			return new PersistentCollectionSerializer(elementType);
		}
		if (HibernateProxy.class.isAssignableFrom(raw)) {
			return new HibernateProxySerializer();
		}
		return null;
	}

	@Override
	public JsonSerializer<?> findCollectionSerializer(
			SerializationConfig config, CollectionType type,
			BeanDescription beanDesc, BeanProperty property,
			TypeSerializer elementTypeSerializer,
			JsonSerializer<Object> elementValueSerializer) {
		Class<?> raw = type.getRawClass();
		// only handle PersistentCollection style collections...
		if (PersistentCollection.class.isAssignableFrom(raw)) {
			/*
			 * And for those, figure out "fallback type"; we MUST have some idea
			 * of type to deserialize, aside from nominal PersistentXxx type.
			 */
			return new PersistentCollectionSerializer(_figureFallbackType(
					config, type));
		}
		return null;
	}

	@Override
	public JsonSerializer<?> findMapSerializer(SerializationConfig config,
			MapType type, BeanDescription beanDesc, BeanProperty property,
			JsonSerializer<Object> keySerializer,
			TypeSerializer elementTypeSerializer,
			JsonSerializer<Object> elementValueSerializer) {
		Class<?> raw = type.getRawClass();
		// 05-Jun-2012, tatu: PersistentMap DOES implement java.util.Map...
		if (PersistentMap.class.isAssignableFrom(raw)) {
			return new PersistentCollectionSerializer(_figureFallbackType(
					config, type));
		}
		return null;
	}

	protected JavaType _figureFallbackType(SerializationConfig config,
			JavaType persistentType) {
		// Alas, PersistentTypes are NOT generics-aware... meaning can't specify
		// parameterization
		Class<?> raw = persistentType.getRawClass();
		TypeFactory tf = config.getTypeFactory();
		if (Map.class.isAssignableFrom(raw)) {
			return tf.constructMapType(Map.class, Object.class, Object.class);
		}
		if (List.class.isAssignableFrom(raw)) {
			return tf.constructCollectionType(List.class, Object.class);
		}
		if (Set.class.isAssignableFrom(raw)) {
			return tf.constructCollectionType(Set.class, Object.class);
		}
		// ok, just Collection of some kind
		return tf.constructCollectionType(Collection.class, Object.class);
	}
}
